package com.stateflow.engine.user.biz.impl;

import com.stateflow.engine.user.biz.dto.req.QueryStateFlowReqDTO;
import com.stateflow.engine.util.SignCheckUtils;
import com.stateflow.facade.common.cache.redis.RedisCache;
import com.stateflow.facade.common.cache.redis.redislock.RedisLock;
import com.stateflow.facade.common.model.req.CommonRequest;
import com.stateflow.facade.common.model.res.CommonResult;
import com.stateflow.facade.common.template.CommonTemplate;
import com.stateflow.facade.common.util.CommonAssert;
import com.stateflow.facade.common.util.DateUtils;
import com.stateflow.facade.common.util.SeriaNumber;
import com.stateflow.engine.config.RedisConfig;
import com.stateflow.facade.common.util.StringUtils;
import com.stateflow.facade.dto.OperatorDTO;
import com.stateflow.facade.dto.StateDTO;
import com.stateflow.facade.dto.StateFlowConfigDTO;
import com.stateflow.engine.core.cache.UserAllDataCache;
import com.stateflow.engine.user.biz.dto.cache.UserAllDataCacheDTO;
import com.stateflow.engine.user.biz.dto.req.RemoveStateFlowReqDTO;
import com.stateflow.engine.user.biz.dto.req.SubStateFlowReqDTO;
import com.stateflow.engine.user.biz.service.CRUDStateFlowService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by weiqingming on 2019/12/5.
 * 用户状态流转配置
 */
@Service
public class CRUDStateFlowServiceImpl implements CRUDStateFlowService, ApplicationRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger(CRUDStateFlowService.class);

    /** Redis缓存通用工具 */
    private RedisCache redisCache;

    @Override
    public CommonResult createStateFlow(CommonRequest request) {
        return CommonTemplate.execute(request, LOGGER, new CommonTemplate() {
            @Override
            protected void check() {
                SubStateFlowReqDTO req = commonCheck(request, SubStateFlowReqDTO.class);
                CommonAssert.isNoBlankStr(req.getAccessKey(),"accessKey不能为空");
                CommonAssert.isNoBlankStr(req.getSign(),"sign必传");
                SignCheckUtils.checkInteriorSign(req, req.getAccessKey());
                checkStateFlowContent(req);
            }

            @Override
            protected CommonResult business() {

                SubStateFlowReqDTO req = getParam();

                try {

                    //上锁
                    RedisLock.lock(redisCache, req.getAccessKey());

                    //生成模型ID
                    StateFlowConfigDTO config = req.getStateFlowConfig();
                    config.setId(SeriaNumber.getNumber());

                    // 设置时间
                    Date date = new Date();
                    config.setCreateDate(DateUtils.dateToStr(date));
                    config.setModifyDate(DateUtils.dateToStr(date));

                    //取数据
                    UserAllDataCacheDTO data = UserAllDataCache.get(req.getAccessKey());
                    CommonAssert.isNoEmptyObj(data, "获取数据失败，请重试");

                    //添加数据
                    if (CollectionUtils.isEmpty(data.getStateFlowConfigs())) {
                        data.setStateFlowConfigs(new LinkedHashMap<>());
                    }
                    data.getStateFlowConfigs().put(config.getId(), config);

                    //提交数据
                    boolean bl = UserAllDataCache.put(req.getAccessKey(), data);
                    CommonAssert.isTrue(bl, "提交数据失败，请重试");

                    return new CommonResult();

                } catch (Throwable e) {

                    throw e;

                } finally {

                    //解锁
                    RedisLock.unLock(redisCache, req.getAccessKey());
                }
            }
        });
    }


    @Override
    public CommonResult modifyStateFlow(CommonRequest request) {
        return CommonTemplate.execute(request, LOGGER, new CommonTemplate() {
            @Override
            protected void check() {
                SubStateFlowReqDTO req = commonCheck(request, SubStateFlowReqDTO.class);
                CommonAssert.isNoBlankStr(req.getAccessKey(),"accessKey不能为空");
                CommonAssert.isNoBlankStr(req.getSign(),"sign必传");
                SignCheckUtils.checkInteriorSign(req, req.getAccessKey());
                checkStateFlowContent(req);
                CommonAssert.isNoBlankStr(req.getStateFlowConfig().getId(), "模型ID必传");
            }

            @Override
            protected CommonResult business() {

                SubStateFlowReqDTO req = getParam();

                try {

                    //上锁
                    RedisLock.lock(redisCache, req.getAccessKey());

                    StateFlowConfigDTO config = req.getStateFlowConfig();

                    //取数据
                    UserAllDataCacheDTO data = UserAllDataCache.get(req.getAccessKey());
                    Map<String, StateFlowConfigDTO> configs = data.getStateFlowConfigs();
                    CommonAssert.isNoEmptyObj(data, "获取数据失败，请重试");
                    CommonAssert.isTrue(configs != null, "数据异常，请重试");
                    CommonAssert.isTrue(configs.containsKey(config.getId()), "没有找到这个ID的模型");

                    // 设置最新编辑时间
                    config.setModifyDate(DateUtils.dateToStr(new Date()));

                    //设置数据，覆盖原数据
                    boolean bl = UserAllDataCache.modifyConfig(data, config);
                    CommonAssert.isTrue(bl, "提交数据失败，请重试");

                    return new CommonResult();

                } catch (Throwable e) {

                    throw e;

                } finally {

                    //解锁
                    RedisLock.unLock(redisCache, req.getAccessKey());
                }


            }
        });
    }

    @Override
    public CommonResult removeStateFlow(CommonRequest request) {
        return CommonTemplate.execute(request, LOGGER, new CommonTemplate() {
            @Override
            protected void check() {
                RemoveStateFlowReqDTO req = commonCheck(request, RemoveStateFlowReqDTO.class);
                CommonAssert.isNoBlankStr(req.getAccessKey(),"accessKey不能为空");
                CommonAssert.isNoBlankStr(req.getId(), "模型ID必传");
                CommonAssert.isNoBlankStr(req.getSign(),"sign必传");
                SignCheckUtils.checkInteriorSign(req, req.getAccessKey(), req.getId());
            }

            @Override
            protected CommonResult business() {

                RemoveStateFlowReqDTO req = getParam();

                try {

                    //上锁
                    RedisLock.lock(redisCache, req.getAccessKey());

                    //取数据
                    UserAllDataCacheDTO data = UserAllDataCache.get(req.getAccessKey());
                    Map<String, StateFlowConfigDTO> configs = data.getStateFlowConfigs();
                    CommonAssert.isNoEmptyObj(data, "获取数据失败，请重试");
                    CommonAssert.isTrue(configs != null, "数据异常，请重试");
                    CommonAssert.isTrue(configs.containsKey(req.getId()), "没有找到这个ID的模型");

                    //删除数据
                    boolean bl = UserAllDataCache.removeConfig(data, req.getId());
                    CommonAssert.isTrue(bl, "提交数据失败，请重试");

                    return new CommonResult();

                } catch (Throwable e) {

                    throw e;

                } finally {

                    //解锁
                    RedisLock.unLock(redisCache, req.getAccessKey());
                }
            }
        });
    }


    @Override
    public CommonResult queryStateFlow(CommonRequest request) {
        return CommonTemplate.execute(request, LOGGER, new CommonTemplate() {
            @Override
            protected void check() {
                QueryStateFlowReqDTO req = commonCheck(request, QueryStateFlowReqDTO.class);
                CommonAssert.isNoBlankStr(req.getAccessKey(),"accessKey不能为空");
                CommonAssert.isNoBlankStr(req.getSign(),"sign必传");
                SignCheckUtils.checkInteriorSign(req, req.getAccessKey());
            }

            @Override
            protected CommonResult business() {
                QueryStateFlowReqDTO req = getParam();

                //取数据
                UserAllDataCacheDTO data = UserAllDataCache.get(req.getAccessKey());
                CommonAssert.isNoEmptyObj(data,"没有找到用户数据");
                Map<String, StateFlowConfigDTO> configs = data.getStateFlowConfigs();
                if (configs == null){
                    return new CommonResult();
                }

                //按ID查询
                Map<String, StateFlowConfigDTO> res = configs;
                if (StringUtils.isNotBlank(req.getConfigId())){
                    StateFlowConfigDTO config = res.get(req.getConfigId());
                    if (config != null){
                        Map<String, StateFlowConfigDTO> m = new LinkedHashMap<>();
                        m.put(config.getId(),config);
                        res = m;
                    }
                }

                //按名称模糊查询
                if (StringUtils.isNotBlank(req.getConfigName())) {
                    Map<String, StateFlowConfigDTO> m = new LinkedHashMap<>();
                    for (Map.Entry<String, StateFlowConfigDTO> item : res.entrySet()) {
                        StateFlowConfigDTO config = item.getValue();
                        if (config.getName().contains(req.getConfigName())) {
                            m.put(item.getKey(),config);
                        }
                    }
                    res = m;
                }

                List<StateFlowConfigDTO> result = res.values().stream().collect(Collectors.toList());
                return new CommonResult(result);

            }
        });
    }

    /**
     * 校验状态流转模型配置内容
     *
     * @param req
     */
    private void checkStateFlowContent(SubStateFlowReqDTO req) {

        StateFlowConfigDTO config = req.getStateFlowConfig();
        CommonAssert.isNoEmptyObj(config, "状态流转模型不能为空");
        CommonAssert.isNoBlankStr(config.getName(), "状态流转模型名称必填");

        if (CollectionUtils.isEmpty(config.getStateFlows())) {
            return;
        }


        // 操作码出现次数标记
        Map<String, Integer> stateStamp = new HashMap<>();

        for (StateDTO item : config.getStateFlows()) {
            CommonAssert.isNoBlankStr(item.getState(), "状态码不能为空");
            CommonAssert.isNoBlankStr(item.getStateName(), "状态名称不能为空");

            Integer sStamp = stateStamp.get(item.getState());
            CommonAssert.isTrue(sStamp == null, item.getState() + " 状态出现重复");
            stateStamp.put(item.getState(), 1);

            if (CollectionUtils.isEmpty(item.getOperators())) {
                continue;
            }

            // 操作码出现记录标记
            Map<String, Integer> operatorStamp = new HashMap<>();

            for (OperatorDTO operatorItem : item.getOperators()) {
                CommonAssert.isNoBlankStr(operatorItem.getOperatorCode(), "操作码不能为空");
                CommonAssert.isNoBlankStr(operatorItem.getOperatorName(), "操作名称不能为空");
                CommonAssert.isNoBlankStr(operatorItem.getNextState(), "目标状态不能为空");
                CommonAssert.isNoBlankStr(operatorItem.getNotifyType(), "通知方式不能为空");
                CommonAssert.isNoBlankStr(operatorItem.getNotifyUrl(), "通知地址不能为空");

                Integer oStamp = operatorStamp.get(operatorItem.getOperatorCode());
                CommonAssert.isTrue(oStamp == null, item.getState() + " 状态下出现重复操作：" + operatorItem.getOperatorCode());
                operatorStamp.put(operatorItem.getOperatorCode(), 1);
            }
        }
    }

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        redisCache = RedisConfig.REDIS_CACHE;
    }
}
